/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-bitwise */
import { extendArray } from './array';
import { extendString } from './string';
import { extendMath } from './math';

declare global {
	type msgData = Array<any>;
	type msgCallbackSend = (rt: boolean, data: any) => void;
	type msgCallback = (rt: boolean, msg: any) => void;
}


extendArray();
extendString();
extendMath();

export class Utils {
	static init(): void {
		// do nothong
	}

	static uuid(): string {
		const s = [];
		const hexDigits = '0123456789abcdef';
		for (let i = 0; i < 36; i++) {
			s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
		}
		s[14] = '4'; // bits 12-15 of the time_hi_and_version field to 0010
		// bits 6-7 of the clock_seq_hi_and_reserved to 01
		// s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
		// eslint-disable-next-line no-multi-assign
		s[8] = s[13] = s[18] = s[23] = '-';

		return s.join('');
	}

	// https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
	static uuidv4(): string {
		return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
			const r = (Math.random() * 16) | 0;
			const v = c === 'x' ? r : (r & 0x3) | 0x8;
			return v.toString(16);
		});
	}

	static randomCode(num: number): string {
		let s = '';
		for (let i = 0; i < num; i++) {
			s += Math.randomInt(0, 10);
		}
		return s;
	}

	static isNumeric(data: string): boolean {
		// eslint-disable-next-line no-restricted-globals
		return !isNaN(Number(data));
	}

	static try2Int(data: string): any {
		if (!this.isNumeric(data)) return data;
		return parseInt(data, 10);
	}

	static try2Number(data: string): any {
		if (!this.isNumeric(data)) return data;
		return parseFloat(data);
	}

	static try2IntFirstnot0(data: string): any {
		if (data[0] === '0') {
			return data.length === 1 ? 0 : data;
		}
		if (!this.isNumeric(data)) return data;
		return parseInt(data, 10);
	}

	static try2NumberFirstnot0(data: string): any {
		if (data[0] === '0') {
			return data.length === 1 ? 0 : data;
		}
		if (!this.isNumeric(data)) return data;
		return parseFloat(data);
	}

	static clone(obj: unknown): any {
		let copy: any;

		// Handle the 3 simple types, and null or undefined
		if (obj == null || typeof obj !== 'object') return obj;

		// Handle Date
		if (obj instanceof Date) {
			copy = new Date();
			copy.setTime(obj.getTime());
			return copy;
		}

		// Handle Array
		if (obj instanceof Array) {
			return obj.map((a: any) => this.clone(a));
		}

		// Handle Object
		if (obj instanceof Object) {
			copy = {};
			for (const attr of Object.keys(obj)) {
				copy[attr] = this.clone(obj[attr]);
			}
			return copy;
		}

		throw new Error("Unable to copy obj! Its type isn't supported.");
	}

	static deepCopy(a: unknown): any {
		return JSON.parse(JSON.stringify(a));
	}

	static shallowCopy(src: unknown, des: unknown): void {
		if (src === des) {
			return;
		}
		if (Array.isArray(src) && Array.isArray(des)) {
			for (let i = 0; i < src.length; i++) {
				des[i] = src[i];
			}
		} else if (typeof src === 'object' && typeof des === 'object') {
			for (const [k, v] of Object.entries(src)) {
				des[k] = v;
			}
		}
	}

	static logFull(msg: unknown): void {
		const util = require('util');
		console.log(util.inspect(msg, false, null, true /* enable colors */));
	}

	static isObjectEmpty(obj: unknown): boolean {
		return !obj || Object.keys(obj).length === 0;
	}

	static lenObject(obj: unknown): number {
		return Object.keys(obj).length;
	}

	// like format("{0},{1}!",'hello,world') => hello,world!
	static format(msg: string, arg: string): string {
		if (!arg) {
			return msg;
		}

		const args = arg.split(',');
		for (let k = 0; k < args.length; k++) {
			msg = msg.replace(`{${k}}`, args[k]);
		}
		return msg;
	}

	static arraysEqualSimple(a: any[], b: any[]): boolean {
		if (a === b) return true;
		if (a == null || b == null) return false;
		if (a.length !== b.length) return false;

		// If you don't care about the order of the elements inside
		// the array, you should sort both arrays here.
		// Please note that calling sort on an array will modify that array.
		// you might want to clone your array first.

		for (let i = 0; i < a.length; ++i) {
			if (a[i] !== b[i]) return false;
		}
		return true;
	}

	static updateInfo(info: unknown, fullkey: string, value: unknown): void {
		let key = fullkey;
		// console.log("MSG_AOI_UPDATE",this.name,key,value)
		while (1) {
			const find = key.indexOf('.');
			if (find < 0) {
				info[key] = value;
				break;
			}
			const left = key.substring(0, find);
			if (!info[left]) {
				info[left] = {};
			}
			info = info[left];
			key = key.substring(find + 1);
		}
	}
} // class Utils
